#include "header.h"

void *connect(void *ptr)														//CONNECT TO ALL OTHER BEACON NODE
{

	//printf("Connect Thread CREATED\n");

	pthread_t readThread;															// Read thread to receive the data coming from each node
	pthread_t writeThread;															// write therad to send the data going to each node


	char hostName[100];
	memset(hostName,'\0',100*sizeof(char));
	uint16_t Port;
	char *beaconNamePort = (char *) ptr;
	////printf("Inside Connect Beacon Name and Port = %s\n",beaconNamePort);
	sscanf(beaconNamePort, "%s %hu",hostName, &Port);								
	hostName[strlen(hostName)+1] = '\0';


	int beaconSock;
	int conClient; 
	struct hostent *he;
	struct sockaddr_in serverAddress;



	beaconSock = socket(AF_INET,SOCK_STREAM,0);									//create socket and connect
	if (beaconSock == -1)
	{
		perror("Create socket\n");
	}
	////printf("Socket Created\n");

	memset(&he,0,sizeof(he));
	he = gethostbyname(hostName);
	memset(&serverAddress,'\0',sizeof(serverAddress));
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)he->h_addr_list[0]));
	serverAddress.sin_port = htons(Port);

	////printf("Retry = %u\n", node.getRetry());
	char hostNamePort[100];
	memset(hostNamePort,'\0',100*sizeof(char));
	sprintf(hostNamePort, "%s:%u",hostName,Port);										//concat senders host name and port by :
	hostNamePort[strlen(hostNamePort)+1] = '\0';
	////printf("hostNamePort = %s\n",hostNamePort);
	
	
	multimap<string,int> tempMap;

	while(1)
	{	
		pthread_mutex_lock(&NeighborList);																//Apply LOCK

		tempMap = node.getNeighborList();
		printf("SIZE = %d\n",	tempMap.size());

		pthread_mutex_unlock(&NeighborList);																//RELEASE LOCK

		multimap<string,int>::iterator it;

		/*for ( it=tempMap.begin(); it != tempMap.end(); it++ )
		{
		    cout <<"Neighbor is"<< (*it).first <<endl;
		}*/
		printf("Find hostNamePort = %s\n",hostNamePort);

		if(tempMap.find(hostNamePort) == tempMap.end())		//IF equal Entry NOT FOUND
		{
			conClient = 0;
			conClient = connect(beaconSock, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr));
			////printf("conClient = %d\n",conClient);
			////printf("Socket FD = %d\n",beaconSock);
			if(conClient >= 0)
			{
				printf("Connected\n");
				break;
			}
			else
			{
				printf("Sleeping...\n");
				////printf("Connecting to %s at port number %hu \n",hostName,Port);
				//sleep(((int)node.getRetry())*1000);
				//perror(" Connect Error\n");
				sleep(node.getRetry());
				printf("Awake\n");
				if(shutdownFlag == true)
				{	
					//printf("Connect Thread EXIT due to Shutdown\n");
					pthread_exit(NULL);
				}
			}
		}
		else																							// If already connected
		{	
			////printf("Closing Socket\n");
			
			close(beaconSock);
			printf("Connect Thread EXIT due to \n");
			pthread_exit(NULL);
		}
	}
	
	////printf("conClient = %d\n",conClient);
	
																							// SEND HELLO HEADER
	
	////printf("Length = %d\n",strlen(node.getHostName()));
	unsigned int dataLength = (uint32_t)(sizeof(uint16_t)+ strlen(node.getHostName()));				// data length for hello
	Header sendHeader = createHeader((uint8_t)helloMessageType, dataLength);
	
	
	
	//printf("Type = 0x%02x\n",sendHeader.messageType);
	//printUOID(sendHeader.uoid);
	////printf("TTL = %hhu\n",sendHeader.ttl);
	////printf("Reserved = %hhu\n",sendHeader.reserved);
	////printf("Datalength = %u\n",sendHeader.dataLength);
	
	char sendheaderBuffer[28];
	memset(sendheaderBuffer,'\0',28);
	memcpy(&sendheaderBuffer[0],&htons(sendHeader.messageType),1);
	memcpy(&sendheaderBuffer[1],sendHeader.uoid,20);
	memcpy(&sendheaderBuffer[21],&htons(sendHeader.ttl),1);
	memcpy(&sendheaderBuffer[22],&htons(sendHeader.reserved),1);
	memcpy(&sendheaderBuffer[23],&htonl(sendHeader.dataLength),4);
	sendheaderBuffer[strlen(sendheaderBuffer)+1] = '\0';
	int sendHeaderBytes = send(beaconSock,&sendheaderBuffer,27,0);																				
	if(sendHeaderBytes != 27)
	{
		//printf("Header was not completely send in CONNECT\n");
		pthread_exit(NULL);
	}
	printf("Header Sent\n");

																				// SEND HELLO MESSAGE

	

	HelloMessage helloMsg = sendHello();
	////printf("port = %hu\n",helloMsg.port);

	
	char sendMessageBuffer[sendHeader.dataLength+1];
	////printf(" HostName ---->%s---\n",helloMsg.hostname);
	memset(sendMessageBuffer,'\0',(sendHeader.dataLength+1)*sizeof(char));
	memcpy(&sendMessageBuffer[0],&htons(helloMsg.port),2);
	memcpy(&sendMessageBuffer[2],(char *)helloMsg.hostname,strlen(helloMsg.hostname));
	sendMessageBuffer[strlen(sendMessageBuffer)+1] = '\0';
	printf("Sent Message is : %s---\n",sendMessageBuffer);
	int sendMsgBytes = send(beaconSock,&sendMessageBuffer,sendHeader.dataLength,0);																				
	if(sendMsgBytes != (int)sendHeader.dataLength)
	{
		//printf("Msg was not completely sent in CONNECT\n");
		pthread_exit(NULL);
	}
	////printf("A\n");
	////printf("send bytes = %d\n",sendHelloMessage);


	Messages msg;
    msg.Header = (char *)malloc(28*sizeof(char));
    memset(msg.Header,'\0',28);
    msg.Data = (char *)malloc(512*sizeof(char));
    memset(msg.Data,'\0',512);
    memcpy(&msg.Header[0],&sendheaderBuffer[0],27);
    memcpy(&msg.Data[0],&helloMsg.port,2);
    memcpy(&msg.Data[2],&helloMsg.hostname[0],13);
    struct timeval t;
    gettimeofday(&t, NULL);
    enterLogMessage(msg,'s',t);






	
	Header recHeader;																			// RECEIVE HELLO HEADER
	
	char recHeaderBuffer[28];
	memset(recHeaderBuffer,'\0',28);
	int recHeaderBytes = recv(beaconSock,&recHeaderBuffer,27,0);
	if(recHeaderBytes != 27)
	{
		//printf("Header was not completely received in CONNECT\n");
		pthread_exit(NULL);
	}
	recHeaderBuffer[strlen(recHeaderBuffer)+1] = '\0';
	printf("Header Received\n");

	recHeader.uoid = (unsigned char *)malloc(20* sizeof(char));
	memcpy(&recHeader.messageType,&ntohs(recHeaderBuffer[0]),1);
	memcpy(recHeader.uoid,&recHeaderBuffer[1],20);
	memcpy(&recHeader.ttl,&ntohs(recHeaderBuffer[21]),1);
	memcpy(&recHeader.reserved,&ntohs(recHeaderBuffer[22]),1);
	memcpy(&recHeader.dataLength,&ntohl(recHeaderBuffer[23]),4);
		
		
	//printf("Type = 0x%02x\n",recHeader.messageType);
	//printUOID(recHeader.uoid);
	////printf("TTL = %hhu\n",recHeader.ttl);
	////printf("Reserved = %hhu\n",recHeader.reserved);
	////printf("Datalength = %u\n",recHeader.dataLength);
																		
	HelloMessage recHello;																		//RECEIVE HELLO MESSAGE
	char recMessageBuffer[recHeader.dataLength+1];
	memset(recMessageBuffer,'\0',(recHeader.dataLength+1)*sizeof(char));
	int recMsgBytes = recv(beaconSock,&recMessageBuffer,recHeader.dataLength,0);	
	if(recMsgBytes !=(int) recHeader.dataLength)
	{
		//printf("Msg was not completely received in CONNECT\n");
		pthread_exit(NULL);
	}
	recMessageBuffer[strlen(recMessageBuffer)+1] = '\0';
	printf("Message Received = %s\n",recMessageBuffer);
		

	recHello.hostname = (char *)malloc(100 * sizeof(char));
	//bzero(recHello.hostname,100 *sizeof(char));
	//char* buffer=new char[100]; delete[] buffer;
	memset(recHello.hostname,'\0',100 *sizeof(char));
	memcpy(&recHello.port,&ntohs(recMessageBuffer[0]),2);
	memcpy(recHello.hostname,&recMessageBuffer[2],recHeader.dataLength-2);
	recHello.hostname[strlen(recHello.hostname)+1] = '\0';
	
	//printf("Received Hello Host name = %s\n",recHello.hostname);
	//printf("Received Hello Port = %hu\n",recHello.port);


	Messages msg1;
    msg1.Header = (char *)malloc(28*sizeof(char));
    memset(msg1.Header,'\0',28);
    msg1.Data = (char *)malloc(512*sizeof(char));
    memset(msg1.Data,'\0',512);
    memcpy(&msg1.Header[0],&recHeaderBuffer[0],27);
    memcpy(&msg.Data[0],&recHello.port,2);
    memcpy(&msg.Data[2],&recHello.hostname[0],13);
    struct timeval t1;    
    gettimeofday(&t1, NULL);
    enterLogMessage(msg1,'r',t1);
	
	pthread_mutex_lock(&NeighborList);																//Apply LOCK

	int createFlag = 0; 
	//char *hostNamePort = (char *)malloc(100*sizeof(char));
	//sprintf(hostNamePort, "%s:%u",recHello.hostname,recHello.port);										//concat senders host name and port by :

	multimap<string,int> tempMultiMap = node.getNeighborList();
	list<int> tempList = node.getRemoveConnectionList();

	if(tempMultiMap.find(hostNamePort)->second == tempMultiMap.end()->second)		//IF equal Entry NOT FOUND
	{
		tempMultiMap.insert(pair<string,int>(hostNamePort,beaconSock));							//if not found, add to neighborList
		createFlag = 1;
	}
	else
	{
																										// IF PRESENT, TIE BREAKER, // As it is already present, the previously added node would be added by client of THIS NODE		
		if(node.getPort() > recHello.port)																							
		{
			////printf("Do nothing\n");
			//Do nothing because there is already a connection made by this node (in Connection Thread)
		}
		else if(node.getPort() < recHello.port)
		{
			int temp = tempMultiMap.find(hostNamePort)->second;
			tempList.push_back(temp);			//add the sockdesc (for read and write thread) to this list which you want to cancel due to duplicate connection


			tempMultiMap.erase(tempMultiMap.find(hostNamePort));			//erase neighbor node with sockdesc
			
			tempMultiMap.insert(pair<string,int>(hostNamePort,beaconSock));			//add neighbor with THIS sockdesc
			createFlag = 1;
		}
		else
		{
			if(strcmp(node.getHostName(),recHello.hostname) > 0)
			{
				////printf("Do nothing\n");
				//Do nothing because there is already a connection made by this node (in Connection Thread)
			}
			else if(strcmp(node.getHostName(),recHello.hostname) < 0)
			{
				int temp = tempMultiMap.find(hostNamePort)->second;
				tempList.push_back(temp);			//add the sockdesc (for read and write thread) to this list which you want to cancel due to duplicate connection


				tempMultiMap.erase(tempMultiMap.find(hostNamePort));			//erase neighbor node with sockdesc
			
				tempMultiMap.insert(pair<string,int>(hostNamePort,beaconSock));			//add neighbor with THIS sockdesc
				createFlag = 1;
			}
		}
	}
	
	node.setNeighborList(tempMultiMap);
	node.setRemoveConnectionList(tempList);
	////printf("SIZE AFTER ADDING = %d\n",	node.getNeighborList().size());
	//multimap<string,int> :: iterator it1;
	//multimap<string,int> temp1 = node.getNeighborList();
	////printf("\n********************************************************\n");
	////printf("\nsize multimap = %d\n",temp1.size());
	//for(it1=temp1.begin();it1!=temp1.end();it1++)
	//	//printf("\nDescriptor is :---> %d\n",(*it1).second);
	pthread_mutex_unlock(&NeighborList);															//Release LOCK

	if(createFlag == 1)																			// if your port number and hostname are less then othernode you have to create threds here
	{

		pthread_mutex_lock(&deleteConnectionLock[beaconSock]);	
		deleteConnection[beaconSock] = false;
		pthread_mutex_unlock(&deleteConnectionLock[beaconSock]);	

		iretReadThread = pthread_create(&readThread, NULL, read, (void *) beaconSock);
		iretWriteThread = pthread_create(&writeThread, NULL, write, (void *)beaconSock);
		iretKeepAliveThread = pthread_create(&keepAliveThread, NULL, keepAlive, (void *)beaconSock);

		//pthread_join( keepAliveThread,NULL);
		//pthread_join( readThread, NULL);
		//pthread_join( writeThread, NULL);
	}
	free(sendMessageBuffer);
	free(recMessageBuffer);
	

	return NULL;
}
